package com.gitee.wsl.coroutines.job

import com.gitee.wsl.coroutines.job.exception.JobExecutionException
import com.gitee.wsl.coroutines.job.sheduler.IScheduler
import com.gitee.wsl.coroutines.job.trigger.ITrigger
import com.gitee.wsl.struct.generator.randomUUID
import kotlin.reflect.KClass


interface IJob {
    /**
     *
     *
     * Called by the `[IScheduler]` when a
     * `[ITrigger]` fires that is associated with the
     * `Job`.
     *
     *
     *
     * The implementation may wish to set a
     * [result][IJobExecutionContext.setResult] object on the
     * [IJobExecutionContext] before this method exits. The result itself is
     * meaningless to Quartz, but may be informative to
     * `[IJobListener]s` or
     * `[ITriggerListener]s` that are watching the job's
     * execution.
     *
     *
     * @throws JobExecutionException
     * if there is an exception while executing the job.
     */
    @Throws(JobExecutionException::class)
    fun execute(context: IJobExecutionContext)
}

inline fun<reified K:IJob> createJob(config:JobBuilder.()->Unit):JobDetail{
    val builder = JobBuilder.newJob(K::class)
    builder.config()
    return builder.build()
}

class JobBuilder
protected constructor() {
    private var m_aKey: JobKey? = null
    private var m_sDescription: String? = null
    private var m_aJobClass: KClass<out IJob>? = null
    private var m_bDurability = false
    private var m_bShouldRecover = false

    private var m_aJobDataMap = JobDataMap()

    /**
     * Produce the `JobDetail` instance defined by this
     * `JobBuilder`.
     *
     * @return the defined JobDetail.
     */
    fun build(): JobDetail {
        val job = JobDetail()

        job.jobClass = m_aJobClass
        job.description = m_sDescription
        if (m_aKey == null) m_aKey = JobKey(randomUUID(), null)
        job.key = m_aKey
        job.isDurable = m_bDurability
        job.shouldRecover = m_bShouldRecover

        if (!m_aJobDataMap.isEmpty()) job.jobDataMap = m_aJobDataMap

        return job
    }

    /**
     * Use a `JobKey` with the given name and default group to identify
     * the JobDetail.
     *
     *
     * If none of the 'withIdentity' methods are set on the JobBuilder, then a
     * random, unique JobKey will be generated.
     *
     *
     * @param name
     * the name element for the Job's JobKey
     * @return the updated JobBuilder
     * @see JobKey
     *
     * @see IJobDetail.getKey
     */
    /*fun withIdentity(name: String): JobBuilder {
        m_aKey = JobKey(name, null)
        return this
    }*/

    /**
     * Use a `JobKey` with the given name and group to identify the
     * JobDetail.
     *
     *
     * If none of the 'withIdentity' methods are set on the JobBuilder, then a
     * random, unique JobKey will be generated.
     *
     *
     * @param name
     * the name element for the Job's JobKey
     * @param group
     * the group element for the Job's JobKey
     * @return the updated JobBuilder
     * @see JobKey
     *
     * @see IJobDetail.getKey
     */
    fun withIdentity(name: String, group: String ?= null): JobBuilder {
        m_aKey = JobKey(name, group)
        return this
    }

    /**
     * Use a `JobKey` to identify the JobDetail.
     *
     *
     * If none of the 'withIdentity' methods are set on the JobBuilder, then a
     * random, unique JobKey will be generated.
     *
     *
     * @param jobKey
     * the Job's JobKey
     * @return the updated JobBuilder
     * @see JobKey
     *
     * @see IJobDetail.getKey
     */
    fun withIdentity(jobKey: JobKey): JobBuilder {
        m_aKey = jobKey
        return this
    }

    /**
     * Set the given (human-meaningful) description of the Job.
     *
     * @param jobDescription
     * the description for the Job
     * @return the updated JobBuilder
     * @see IJobDetail.getDescription
     */
    fun withDescription(jobDescription: String): JobBuilder {
        m_sDescription = jobDescription
        return this
    }

    /**
     * Set the class which will be instantiated and executed when a Trigger fires
     * that is associated with this JobDetail.
     *
     * @param jobClazz
     * a class implementing the Job interface.
     * @return the updated JobBuilder
     * @see IJobDetail.getJobClass
     */
    fun ofType(jobClazz: KClass<out IJob>): JobBuilder {
        m_aJobClass = jobClazz
        return this
    }

    /**
     * Instructs the `Scheduler` whether or not the `Job`
     * should be re-executed if a 'recovery' or 'fail-over' situation is
     * encountered.
     *
     *
     * If not explicitly set, the default value is `false`.
     *
     *
     * @return the updated JobBuilder
     * @see IJobDetail.requestsRecovery
     */
    fun requestRecovery(): JobBuilder {
        m_bShouldRecover = true
        return this
    }

    /**
     * Instructs the `Scheduler` whether or not the `Job`
     * should be re-executed if a 'recovery' or 'fail-over' situation is
     * encountered.
     *
     *
     * If not explicitly set, the default value is `false`.
     *
     *
     * @param jobShouldRecover
     * the desired setting
     * @return the updated JobBuilder
     */
    fun requestRecovery(jobShouldRecover: Boolean): JobBuilder {
        m_bShouldRecover = jobShouldRecover
        return this
    }

    /**
     * Whether or not the `Job` should remain stored after it is
     * orphaned (no `[ITrigger]s` point to it).
     *
     *
     * If not explicitly set, the default value is `false` - this
     * method sets the value to `true`.
     *
     *
     * @return the updated JobBuilder
     * @see IJobDetail.isDurable
     */
    fun storeDurably(): JobBuilder {
        m_bDurability = true
        return this
    }

    /**
     * Whether or not the `Job` should remain stored after it is
     * orphaned (no `[ITrigger]s` point to it).
     *
     *
     * If not explicitly set, the default value is `false`.
     *
     *
     * @param jobDurability
     * the value to set for the durability property.
     * @return the updated JobBuilder
     * @see IJobDetail.isDurable
     */
    fun storeDurably(jobDurability: Boolean): JobBuilder {
        m_bDurability = jobDurability
        return this
    }

    /**
     * Add the given key-value pair to the JobDetail's [JobDataMap].
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(dataKey: String, value: String): JobBuilder {
        m_aJobDataMap[dataKey] = value
        return this
    }

    /**
     * Add the given key-value pair to the JobDetail's [JobDataMap].
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(dataKey: String, value: Int): JobBuilder {
        m_aJobDataMap[dataKey] = value
        return this
    }

    /**
     * Add the given key-value pair to the JobDetail's [JobDataMap].
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(dataKey: String, value: Long): JobBuilder {
        m_aJobDataMap[dataKey] = value
        return this
    }

    /**
     * Add the given key-value pair to the JobDetail's [JobDataMap].
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(dataKey: String, value: Float): JobBuilder {
        m_aJobDataMap[dataKey] = value
        return this
    }

    /**
     * Add the given key-value pair to the JobDetail's [JobDataMap].
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(dataKey: String, value: Double): JobBuilder {
        m_aJobDataMap[dataKey] = value
        return this
    }

    /**
     * Add the given key-value pair to the JobDetail's [JobDataMap].
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(dataKey: String, value: Boolean): JobBuilder {
        m_aJobDataMap[dataKey] = value
        return this
    }

    /**
     * Add all the data from the given [JobDataMap] to the
     * `JobDetail`'s `JobDataMap`.
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun usingJobData(newJobDataMap: JobDataMap): JobBuilder {
        m_aJobDataMap.putAll(newJobDataMap)
        return this
    }

    /**
     * Replace the `JobDetail`'s [JobDataMap] with the given
     * `JobDataMap`.
     *
     * @return the updated JobBuilder
     * @see IJobDetail.getJobDataMap
     */
    fun setJobData(newJobDataMap: JobDataMap): JobBuilder {
        m_aJobDataMap = newJobDataMap
        return this
    }

    companion object {
        /**
         * Create a JobBuilder with which to define a `JobDetail`.
         *
         * @return a new JobBuilder
         */
        fun newJob(): JobBuilder {
            return JobBuilder()
        }

        /**
         * Create a JobBuilder with which to define a `JobDetail`, and set
         * the class name of the `Job` to be executed.
         *
         * @return a new JobBuilder
         */
        fun newJob(jobClass: KClass<out IJob>): JobBuilder {
            val b = JobBuilder()
            b.ofType(jobClass)
            return b
        }
    }
}


